package algorithm;

import io.OutputWriter;
import java.io.IOException;
import pac.SearchDatastructure;
import datastructures.MapfileWrapper;
import datastructures.SNPInfo;


public class RandomDraws {

	private SearchDatastructure searcher;
	private OutputWriter outputter;
	private MapfileWrapper snpSet;
	private long numberOfDraws;
	private int drawMode;
	private double pairDistance;
	
	public RandomDraws(SearchDatastructure I, OutputWriter O) throws IOException {
		searcher = I;
		outputter = O;
		snpSet = new MapfileWrapper(searcher.snpList);
		numberOfDraws = searcher.userParams.getNumberOfDraws();
		drawMode = searcher.userParams.getAllOrTransOrCis();
		pairDistance = searcher.userParams.getBlockSizeBp();
	}
	
	public int findAnAppropriateVariableToPairWithThisOne (int variable1) {
		
		SNPInfo snpinfo1 = snpSet.getSNPInfo( (int) Math.floor(variable1/2) );
		
		int variable2 = (int) Math.floor( Math.random()*( 2*snpSet.getNumberOfSNPs() ) ); 
		SNPInfo snpinfo2 = snpSet.getSNPInfo( (int) Math.floor(variable2/2) );
		
		//any pairs are okay
		if(drawMode == 0) {
			while ( snpinfo2== null 
						&& (int) Math.floor(variable2/2) == (int) Math.floor(variable1/2) ) 
			{
				variable2 = (int) Math.floor( Math.random()*( 2*snpSet.getNumberOfSNPs() ) ); 
				snpinfo2 = snpSet.getSNPInfo( (int) Math.floor(variable2/2) );
			}
			return variable2;
		}
				
		//in-trans pairs only
		if(drawMode == 1) { 
			while(  snpinfo2== null || 
						( 
								snpinfo1.getChromosome() == snpinfo2.getChromosome() 
								&& Math.abs( snpinfo1.getBp_distance() - snpinfo2.getBp_distance() ) < pairDistance 
						)
		            ) 
			{
				variable2 = (int) Math.floor( Math.random()*( 2*snpSet.getNumberOfSNPs() ) ); 
				snpinfo2 = snpSet.getSNPInfo( (int) Math.floor(variable2/2) );
			}
			return variable2;
		}
		
		//in-cis pairs only - cannot assume sorted map file
		if(drawMode == 2) { 		
			int counter = 0; //keep tabs on how many draws you make to find a pair
			
			while( 	snpinfo2 == null 
						|| ( snpinfo1.getChromosome() != snpinfo2.getChromosome() ) 
						|| Math.abs( snpinfo1.getBp_distance() - snpinfo2.getBp_distance() ) > pairDistance
						|| ( snpinfo1.getBp_distance() == snpinfo2.getBp_distance() ) 
						) 
			{
				variable2 = (int) Math.floor( Math.random()*( 2*snpSet.getNumberOfSNPs() ) ); 
				snpinfo2 = snpSet.getSNPInfo( (int) Math.floor(variable2/2) );
				
				counter++;
				if(counter > 100000 ) { 
					//one too many draws for this SNP. Let's try another one.
					System.out.println("Skipping.");
					return -1;
				}
				
			}
			
			return variable2;
		}
		
		return variable2;
	}
	
	
	/*
	 * Draw random pairs from your data.
	 */
	public void drawPairs () throws IOException {			
		int[] variables = new int[2];		
	
		for(int draw=0; draw<numberOfDraws; draw++) 
		{
			if( draw*100 % numberOfDraws == 0 )
				System.out.println("Finished " + draw*100 / numberOfDraws + "%");
			
			variables[0] = (int) Math.floor( Math.random()*( 2*snpSet.getNumberOfSNPs() ) );
			SNPInfo snpinfo1 = snpSet.getSNPInfo( (int) Math.floor(variables[0]/2) );
			
			if(snpinfo1 == null) {
				draw--;
				continue;
			}
			
			variables[1] = findAnAppropriateVariableToPairWithThisOne(variables[0]);
			//System.out.println("Found" + draw);
			
			if ( ! processValidPairs(variables) )  
				draw--;
			
			//System.out.println("Done " + draw);
			//System.out.println();
			
		}
		
		System.out.println("Finished.");
		outputter.Close();
	}
	
	

	public boolean processValidPairs (int[] variables) {
			
			if(variables[0] == -1 || variables[1] == -1)
				return false;
			
			int order = variables.length;
			
			//2.5 : establish expected and observed on number of case carriers
			int[][][] noOfCaseCarriers = searcher.snpIndices.getObservedCaseCarriers(searcher.userParams, variables);
			double pvalueCaseOnly = searcher.significance.TestCasesOnly ( order, noOfCaseCarriers );
			
			int[][][] noOfControlCarriers = searcher.snpIndices.getObservedControlCarriers(searcher.userParams, variables);
			double pvalueCaseControl = searcher.significance.TestCasesVsControls ( order, noOfCaseCarriers, noOfControlCarriers );
			
			if ( 	(variables[0] == variables[1]) 
					|| (variables[0]-variables[1] == 1 && variables[0]%2==1 && variables[1]%2==0)
					|| (variables[1]-variables[0] == 1 && variables[1]%2==1 && variables[0]%2==0) 
				)
				pvalueCaseControl = 1;
				
			//depending on whether you are asked for a region, or for a list of specific entries, output accordingly.
			outputter.WriteOutput(variables, noOfCaseCarriers, pvalueCaseOnly, noOfControlCarriers, pvalueCaseControl);
			
			return true;
	}
	
	
}
